package com.fattyCorps.battle.service.battle.effect;

import cn.hutool.core.util.RandomUtil;
import com.fattyCorps.battle.service.battle.Battle;
import com.fattyCorps.battle.service.battle.behavior.TargetSelector;
import com.fattyCorps.battle.service.battle.data.BattleHero;
import com.fattyCorps.common.enums.battle.EBehaviorEffectType;
import com.fattyCorps.common.enums.battle.ESkillTargetType;
import com.fattyCorps.common.enums.battle.calc.ECalcType;
import com.fattyCorps.common.enums.battle.prop.EDynamicProp;
import com.fattyCorps.common.enums.battle.prop.EStaticProp;
import com.fattyCorps.common.obj.config.BehaviorEffectMould;
import com.fattyCorps.common.packet.hero.Hero;
import lombok.Getter;
import lombok.Setter;

import java.math.BigDecimal;

// 效果影响
// todo: 需要有个临时的Effect用于一次行为的计算而不是现在这样上来就创建好，clone是个临时方法
public class Effect {
    Battle battle;
    @Getter
    BehaviorEffectMould mould;
    @Getter
    @Setter
    BattleHero trigger;
    @Getter
    @Setter
    BattleHero target;
    @Getter
    boolean isBuff;
    // 客户端是否显示该效果
    @Getter
    @Setter
    boolean show = true;

    public Effect(Battle battle, BehaviorEffectMould mould, BattleHero trigger, boolean isBuff) {
        this.battle = battle;
        this.mould = mould;
        this.trigger = trigger;
        this.isBuff = isBuff;
    }

    public static Effect makeNorAtt(Battle battle, BattleHero caster) {
        BehaviorEffectMould mould = new BehaviorEffectMould();
        mould.setName("默认攻击效果");
        mould.setType(EBehaviorEffectType.DynamicProp);
        mould.setDynamicCalcTargetProp(EDynamicProp.Hp);
        mould.setDynamicCalcSrcProp(EStaticProp.Att);
        mould.setDynamicCalcType(ECalcType.PerValue);
        mould.setDynamicCalcValue("100");

        Effect effectNorAtt = new Effect(battle, mould, caster, false);
        effectNorAtt.setTarget(TargetSelector.select(battle, caster, ESkillTargetType.FirstEnemy).get(0));

        return effectNorAtt;
    }

    public static Effect makeAddEnergy(Battle battle, BattleHero caster, BattleHero target, int value) {
        return makeAddEnergy(battle, caster, target, value, false);
    }

    public static Effect makeAddEnergy(Battle battle, BattleHero caster, BattleHero target, int value, boolean sortTop) {
        BehaviorEffectMould mould = new BehaviorEffectMould();
        mould.setName("增加能量");
        mould.setType(EBehaviorEffectType.DynamicProp);
        mould.setDynamicCalcTargetProp(EDynamicProp.Energy);
        mould.setDynamicCalcType(ECalcType.FixedValue);
        mould.setDynamicCalcValue("" + value);

        if (sortTop) {
            mould.setSort(999);
        }

        Effect e = new Effect(battle, mould, caster, false);
        e.setTarget(target);

        return e;
    }

    public Effect clone() {
        Effect e = new Effect(this.battle, this.mould, this.trigger, this.isBuff);
        e.target = this.target;
        return e;
    }

    public static class DamageRet {
        public long damage;
        public boolean isCritical;
    }

    // 计算伤害
    /**
     * 护甲减伤率:由〈护甲〉带来的伤害减免，计算公式为［护甲/（200+20*（等级-1））］
     * 基础伤害=基础属性类型的值(攻击/生命...)x技能输出率x技能伤害率x｛［1-（护甲减伤率-护甲减伤率*破甲率）］+总神圣伤害率*0.7｝*（1-减伤率）
     *
     * 如果暴击：伤害=基础伤害x（1.5+2x暴击伤害）暴击伤害最大为100%
     * 如果格挡：伤害=基础伤害x0.7
     */
    // todo: buff伤害不能暴击
    public DamageRet getDamage() {
        if (!mould.getType().equals(EBehaviorEffectType.DynamicProp)) {
            throw new RuntimeException("只有伤害类效果才能计算伤害");
        }

        boolean isCritical = isCritical();
        boolean isParry = isParry();

        Hero trgHero = trigger.getHero();
        Hero tgtHero = target.getHero();

        String calcPropValStr = trgHero.getPropComp().getProp(mould.getDynamicCalcSrcProp());
        BigDecimal propDamage = null;

        switch (mould.getDynamicCalcType()) {
            case PerValue:
                // 配置是放大了100倍的值
                BigDecimal per = new BigDecimal(mould.getDynamicCalcValue()).divide(BigDecimal.valueOf(EStaticProp.AMPLIFY_CFG));
                propDamage = per.multiply(new BigDecimal(calcPropValStr));
                break;
            case FixedValue:
                propDamage = new BigDecimal(mould.getDynamicCalcValue());
                break;
        }

        // 基础属性类型的值(攻击/生命...)x技能输出率x技能伤害率
        BigDecimal damageBase = propDamage.multiply(new BigDecimal(trgHero.getPropComp().getSkillDamagePer()).add(BigDecimal.valueOf(1)));

        // 护甲减伤率:由〈护甲〉带来的伤害减免，计算公式为［护甲/（200+20*（等级-1））］
        BigDecimal defReducePer = new BigDecimal(tgtHero.getPropComp().getDef()).
                divide(BigDecimal.valueOf(200).add(BigDecimal.valueOf(20).
                        multiply(BigDecimal.valueOf(tgtHero.getLv() - 1))), BigDecimal.ROUND_FLOOR);

        // ｛［1-（护甲减伤率-护甲减伤率*破甲率）］+总神圣伤害率*0.7｝
        BigDecimal extraPer = BigDecimal.valueOf(1).subtract(defReducePer.subtract(defReducePer.multiply(
                propStrToBD(trgHero.getPropComp().getArmorBreakPer())))).add(propStrToBD(trgHero.getPropComp().getSacredDamagePer()).multiply(
                        BigDecimal.valueOf(0.7)));

        BigDecimal damage = damageBase.multiply(extraPer);
        if (isCritical) {
            // 伤害=基础伤害x（1.5+2x暴击伤害）暴击伤害最大为100%
            int critDamagePer = 0;
            if (Float.valueOf(trgHero.getPropComp().getCriticalDamagePer()) > 100.0f) {
                critDamagePer = 100;
            }

            damage = damage.multiply(BigDecimal.valueOf(1.5).add(BigDecimal.valueOf(2).multiply(BigDecimal.valueOf(critDamagePer))));
        }

        if (isParry) {
            damage = damage.multiply(BigDecimal.valueOf(0.7));
        }

        DamageRet ret = new DamageRet();
        ret.damage = damage.longValue();
        ret.isCritical = isCritical;
        return ret;
    }

    private BigDecimal propStrToBD(String propStr) {
        return new BigDecimal(propStr).divide(BigDecimal.valueOf(100));
    }

    // 是否格挡
    private boolean isParry() {
        // 格挡=攻击者精准-目标格挡 后再随机
        BigDecimal triggerPrecise = new BigDecimal(trigger.getHero().getPropComp().getPrecisePer());
        BigDecimal targetParry = new BigDecimal(target.getHero().getPropComp().getParryPer());
        return isTrgPer(triggerPrecise.subtract(targetParry).toString());
    }

    // 是否暴击
    private boolean isCritical() {
        return isTrgPer(trigger.getHero().getPropComp().getCriticalPer());
    }

    private boolean isTrgPer(String propPer) {
        // 实际计算时放大10000倍
        final int amplify = 10000;

        BigDecimal bProp = new BigDecimal(propPer);
        Integer propAmplify = bProp.multiply(BigDecimal.valueOf(EStaticProp.AMPLIFY_CFG)).intValue();
        return RandomUtil.randomInt(1, amplify) <= propAmplify;
    }
}
